import {expect} from '@jest/globals';
import * as compressing from 'compressing';
import fs from 'fs-extra';
import path from 'path';
import { t, ReadEntry } from 'tar';

import {hvigorOptions, initExecuteCommand, runHvigorAsync} from '../common/common';

import {APIVersion} from './project-builder/models/constants';
import {Project} from './project-builder/models/project-models';
import {ICaseRecord} from './report-builder/report-models';
import {TestCaseFun} from './task-builder/test-case';
import {addOhPackageDps, commonJson5Operation} from './common-fun';

export const testHar = async (project: Project, buildMode: string) => {
  const harModuleName = 'staticNativeCLibrary';
  const mod = project.getModule(harModuleName);
  const nativeIndexName = path.basename(fs.realpathSync.native(path.join(mod.moduleRootPath(), 'Index.ets')), '.ets');
  const debugModeTestArtifactPaths = [
    `package/${nativeIndexName}.ets`,
    'package/src/main/module.json',
    'package/src/main/ets/components/mainpage/MainPage.ets',
    'package/libs/arm64-v8a/libc++_shared.so',
    'package/libs/arm64-v8a/libstaticNativeCLibrary.so'
  ];
  const releaseModeTestArtifactPaths = [
    `package/${nativeIndexName}.js`,
    'package/src/main/ets/components/mainpage/MainPage.js',
    `package/${nativeIndexName}.d.ets`,
    'package/src/main/module.json',
    'package/src/main/ets/components/mainpage/MainPage.d.ets',
    'package/libs/arm64-v8a/libc++_shared.so',
    'package/libs/arm64-v8a/libstaticNativeCLibrary.so'
  ];
  const set: Set<string> = new Set();
  const testArtifactPaths = buildMode === 'debug' ? debugModeTestArtifactPaths : releaseModeTestArtifactPaths;
  const harPath = path.resolve(mod.moduleRootPath(), 'build', 'default', 'outputs', 'default', `${harModuleName}.har`);

  await t({
    file: harPath,
    onentry(entry: ReadEntry) {
      set.add(entry.path);
    }
  });
  testArtifactPaths.forEach(p => {
    if (!set.has(p)) {
      throw new Error(`buildMode: ${buildMode}，module: ${harModuleName}，构建har产物: ${p} 丢失`);
    }
  });
  set.clear();
};

const getRoots = (moduleRoot: string, artifactName: string) => {
  const artifactRoot = path.resolve(moduleRoot, 'build', 'default', 'outputs', 'default');
  const unzipRoot = path.join(moduleRoot, '_outputs', path.basename(artifactName, path.extname(artifactName)));
  const artifactPath = path.join(artifactRoot, artifactName);

  return { artifactPath, unzipRoot, artifactRoot };
};

// 测试entry模块的hap
const testEntryHap = async (project: Project, buildMode: string) => {
  const debugModeTestArtifactPaths = [
    'ets/modules.abc',
    'ets/commons/test.json',
    'module.json',
    'resources.index',
    'pack.info',
    'ets/sourceMaps.map',
    'libs/arm64-v8a/libc++_shared.so',
    'libs/arm64-v8a/libstaticNativeCLibrary.so'
  ];
  const releaseModeTestArtifactPaths = [
    'ets/modules.abc',
    'ets/commons/test.json',
    'module.json',
    'resources.index',
    'pack.info',
    'libs/arm64-v8a/libc++_shared.so',
    'libs/arm64-v8a/libstaticNativeCLibrary.so'
  ];
  const mod = project.getModule('entry');
  const testArtifactPaths = buildMode === 'debug' ? debugModeTestArtifactPaths : releaseModeTestArtifactPaths;
  const { artifactPath, unzipRoot } = getRoots(mod.moduleRootPath(), 'entry-default-unsigned.hap');

  await compressing.zip.uncompress(artifactPath, unzipRoot);
  testArtifactPaths.forEach(p => {
    if (!fs.existsSync(path.resolve(unzipRoot, p))) {
      throw new Error(`buildMode: ${buildMode}，module: entry，构建hap产物: ${p} 丢失`);
    }
  });
  await fs.remove(unzipRoot);
};

// 测试hsp模块
const testHsp = async (project: Project, buildMode: string) => {
  const debugModeTestArtifactPaths = [
    'ets/modules.abc',
    'module.json',
    'resources.index',
    'pack.info',
    'ets/sourceMaps.map'
  ];
  const releaseModeTestArtifactPaths = [
    'ets/modules.abc',
    'module.json',
    'resources.index',
    'pack.info'
  ];
  const mod = project.getModule('shareLibrary');
  const testArtifactPaths = buildMode === 'debug' ? debugModeTestArtifactPaths : releaseModeTestArtifactPaths;
  const { artifactPath, unzipRoot } = getRoots(mod.moduleRootPath(), 'shareLibrary-default-unsigned.hsp');

  await compressing.zip.uncompress(artifactPath, unzipRoot);
  testArtifactPaths.forEach(p => {
    if (!fs.existsSync(path.resolve(unzipRoot, p))) {
      throw new Error(`buildMode: ${buildMode}，module: shareLibrary，构建hsp产物: ${p} 丢失`);
    }
  });
  await fs.remove(unzipRoot);
};

// 测试 debug 产物
const testHosDebugModeArtifact = async (project: Project, options: hvigorOptions, record: ICaseRecord) => {
  const buildMode = 'debug';
  const result = await runHvigorAsync(
    project.projectRootPath(),
    initExecuteCommand({ ...options, buildMode })
  );
  record.addStep('增量模式下，buildMode为debug，保证构建产物不丢失', result);
  expect(result.stdout).toContain('BUILD SUCCESSFUL');

  await testEntryHap(project, buildMode);
  await testHsp(project, buildMode);
  await testHar(project, buildMode);
};

// 测试 release 产物
const testHosReleaseModeArtifact = async (project: Project, options: hvigorOptions, record: ICaseRecord) => {
  const buildMode = 'release';
  const result = await runHvigorAsync(
    project.projectRootPath(),
    initExecuteCommand({ ...options, buildMode })
  );
  record.addStep('增量模式下，buildMode为release，保证构建产物不丢失', result);
  expect(result.stdout).toContain('BUILD SUCCESSFUL');

  await testEntryHap(project, buildMode);
  await testHsp(project, buildMode);
  await testHar(project, buildMode);
};

export const testHosArtifact: TestCaseFun =  async (project, taskConfig, record) => {
  const options: hvigorOptions = {
    mode: 'module',
    product: 'default',
    daemon: true,
    tasks: ['assembleHap', 'assembleHsp', 'assembleHar']
  };

  // DTS2023120116951
  await commonJson5Operation(
    path.join(project.getModule('staticNativeCLibrary').moduleRootPath(), 'build-profile.json5'),
    json => {
      json.buildOption.externalNativeOptions = {
        path: './src/main/cpp/CMakeLists.txt',
        arguments: '',
        cppFlags: ''
      };
      return json;
    }
  );
  const cMakePath = path.join(project.getModule('staticNativeCLibrary').moduleSrcMainPath(), 'cpp', 'CMakeLists.txt');
  fs.writeFileSync(cMakePath, fs.readFileSync(cMakePath, 'utf-8').replace(/\(library/g, '(staticNativeCLibrary'));
  await addOhPackageDps(project.getModule('entry').moduleRootPath(), {
    'staticNativeCLibrary': '../staticNativeCLibrary'
  });

  // DTS2024010901294，目前只针对api11做了修复，api10修复后移除判断逻辑
  if (project.option.apiVersion === APIVersion.API11) {
    await fs.copy(
      path.resolve(__dirname, '../../resources/module/stage/app/shareLibrary/LocationsPointConstant.ts'),
      path.join(project.getModule('shareLibrary').moduleSrcMainPath(), 'ets', 'LocationsPointConstant.ts')
    );
    await fs.appendFile(
      path.join(project.getModule('shareLibrary').moduleRootPath(), 'Index.ets'),
      '\nexport { LocationsPointConstant } from "./src/main/ets/LocationsPointConstant"'
    );
  }

  // dts: DTS2023121410626
  const mod = project.getModule('entry');
  const testJsonPath = path.join(mod.moduleSrcMainPath(), 'ets', 'commons', 'test.json');
  fs.ensureFileSync(testJsonPath);
  fs.writeJSONSync(testJsonPath, { devices: ['phone'] });

  // 用于压测
  const times = 1;
  for (let i = 0; i < times; i++) {
    // 首次构建，使用debug模式
    await testHosDebugModeArtifact(project, options, record);
    await testHosReleaseModeArtifact(project, options, record);
  }

  // DTS2024013009894
  await commonJson5Operation(
    path.join(project.getModule('staticNativeCLibrary').moduleRootPath(), 'oh-package.json5'),
    json => {
      json.version = '2.0.0';
      return json;
    }
  );
  await testHosReleaseModeArtifact(project, options, record);
};